/**
 * 
 */
package cn.com.easy.permission.service.system.urlreserve;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.annotation.PostConstruct;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;

import cn.com.easy.permission.dao.ResourceDao;
import cn.com.easy.permission.model.ResourceModel;
import cn.com.easy.permission.service.system.urlreserve.anotion.UrlReserveAnotion;
import cn.com.easy.utils.FastJSONUtils;

import com.google.common.collect.Lists;

/**
 * 反射 SpringMVC Controller类上及方法上的RequestMapping 和 PermissionAnotion(主要是权限名称)的注解<br>
 * 类上的权限不能重复
 * 
 * @author nibili 2015年4月1日下午3:10:15
 * 
 */
@Service
public class UrlReserveService {

	private Logger logger = LoggerFactory.getLogger(UrlReserveService.class);

	/** Controller类，所在的包路径 */
	// @Value("${controllerPackagePath}")
	private String controllerPackagePath = "cn.com.platform.web.manage.controller";

	/** 认证排除不过滤的链接 */
	@Value("${manage.external.authentication.filter.url}")
	private String externalAuthenticationUrlsString;
	/** 授权排除不过滤的链接 */
	@Value("${manage.external.authorization.filter.url}")
	private String externalAuthorizationUrlsString;

	/** 认证要排除的url列表 */
	private List<String> externalUrls;

	/** 权限表dao */
	@Autowired
	private ResourceDao resourceDao;

	@PostConstruct
	public void ini() {
		externalUrls = Lists.newArrayList(externalAuthenticationUrlsString.split(","));

		List<String> list = Lists.newArrayList(externalAuthorizationUrlsString.split(","));
		for (String tempString : list) {
			externalUrls.add(tempString.replace(".html", ""));
		}

	}

	public static void main(String[] args) throws Exception {
		UrlReserveService urlReserveService = new UrlReserveService();
		System.out.print(FastJSONUtils.toJsonString(urlReserveService.getUrlResources()));

	}

	/**
	 * 获取所有url资源，在数据库中的url资源 和 排除验证和授权都不要
	 * 
	 * @return
	 * @throws Exception
	 * @auth nibili 2015年12月13日 上午1:39:43
	 */
	public List<UrlResourceEntity> getUrlResources() throws Exception {
		// 方法上的注解
		List<UrlResourceEntity> allMethodPermissions = Lists.newArrayList();
		List<Class<?>> classes = this.getClasses("cn.com.easy.permission.web.controller");
		classes.addAll(this.getClasses(controllerPackagePath));
		for (Class<?> clas : classes) {
			// 方法上的注解
			List<UrlResourceEntity> methodPermissions = this.getRequestMappingMethodAnotionValue(clas);
			allMethodPermissions.addAll(methodPermissions);
		}
		// 遍历排除 在数据库中的url资源 和 排除验证和授权的url
		List<String> urlList = this.getPermissions(resourceDao.findAll());
		urlList.addAll(externalUrls);
		for (java.util.Iterator<UrlResourceEntity> it = allMethodPermissions.iterator(); it.hasNext();) {
			UrlResourceEntity urlResourceEntity = it.next();
			String path = urlResourceEntity.getPath();
			if (path.startsWith("/manage") == false || urlList.contains(path) == true || urlList.contains(path) == true) {
				// 存在于数据库中或排除验证和授权的url，移除掉
				it.remove();
			}
		}

		return allMethodPermissions;
	}

	/**
	 * 获取url权限
	 * 
	 * @param request
	 * @author nibili 2015年12月11日
	 */
	private List<String> getPermissions(List<ResourceModel> resourceModels) {
		List<String> urls = Lists.newArrayList();
		if (CollectionUtils.isNotEmpty(resourceModels) == true) {
			for (ResourceModel resourceModel : resourceModels) {
				if (StringUtils.isNotBlank(resourceModel.getUrl()) == true) {
					// 添加到权限集合中
					urls.add(resourceModel.getUrl());
				}
			}
		}
		return urls;
	}

	// /**
	// *
	// * @throws Exception
	// * @auth nibili 2015年4月12日 下午10:26:58
	// */
	// @PostConstruct
	// @Transactional(rollbackFor = Exception.class)
	// public void init() throws Exception {
	// // 所有类上标注的权限，这里的设计是，类开的权限不能重复
	// List<String> allClazzPermissions = Lists.newArrayList();
	// List<Class<?>> classes = this.getClasses(controllerPackagePath);
	// for (Class<?> clas : classes) {
	// // 类上权限
	// List<PermissionEntity> clazzPermissions =
	// this.getRequestMappingClassAnotionValue(clas);
	// // 方法上的注解
	// List<PermissionEntity> methodPermissions =
	// this.getRequestMappingMethodAnotionValue(clas);
	// // 遍历 类上的权限
	// for (PermissionEntity clazzPermission : clazzPermissions) {
	// if (allClazzPermissions.contains(clazzPermission.getPath()) == true) {
	// // 重复的类的权限，抛出异常
	// throw new RuntimeException("重复的根权限，path:" + clazzPermission.getPath());
	// } else {
	// allClazzPermissions.add(clazzPermission.getPath());
	// }
	// // 根权限
	// ResourceModel rootPermissionModel =
	// permissionDao.findByPathAndParentId(clazzPermission.getPath(),
	// ResourceModel.ROOT_NODE_PARENT_ID);
	// if (rootPermissionModel == null) {
	// rootPermissionModel = new ResourceModel();
	// rootPermissionModel.setParentId(ResourceModel.ROOT_NODE_PARENT_ID);
	// }
	// rootPermissionModel.setName(clazzPermission.getName());
	// rootPermissionModel.setPath(clazzPermission.getPath());
	// rootPermissionModel = this.permissionDao.save(rootPermissionModel);
	// // 子权限
	// Long parentId = rootPermissionModel.getId();
	// for (PermissionEntity methodPermission : methodPermissions) {
	// String methodPermissionPath = methodPermission.getPath();
	// if (methodPermission.getPath().endsWith("/") == false) {
	// methodPermissionPath = methodPermissionPath + "/";
	// }
	// String subPath = rootPermissionModel.getPath() + methodPermissionPath;
	// ResourceModel subPermissionModel =
	// permissionDao.findByPathAndParentId(subPath, parentId);
	// if (subPermissionModel == null) {
	// subPermissionModel = new ResourceModel();
	// subPermissionModel.setParentId(parentId);
	// }
	// subPermissionModel.setName(methodPermission.getName());
	// subPermissionModel.setPath(subPath);
	// this.permissionDao.save(subPermissionModel);
	// }
	// }
	// }
	//
	// }
	// /**
	// * 获取【类】注解中的内容
	// *
	// * @param clazz
	// * @return 类权限集合(可能有多个路径)
	// * @throws Exception
	// * @auth nibili 2015年4月1日 下午11:16:36
	// */
	// private List<UrlResourceEntity>
	// getRequestMappingClassAnotionValue(Class<?> clazz) throws Exception {
	// if (clazz.isAnnotationPresent(RequestMapping.class) == true) {
	// // 权限集合
	// List<UrlResourceEntity> list = Lists.newArrayList();
	// // 权限名称
	// String name = "";
	// if (clazz.isAnnotationPresent(PermissionAnotion.class) == true) {
	// PermissionAnotion permissionAnotion =
	// clazz.getAnnotation(PermissionAnotion.class);
	// name = permissionAnotion.value();
	// }
	// // 权限路径
	// RequestMapping classAnnotation =
	// clazz.getAnnotation(RequestMapping.class);
	// String[] paths = classAnnotation.value();
	// UrlResourceEntity permissionEntity;
	// for (String path : paths) {
	// permissionEntity = new UrlResourceEntity();
	// permissionEntity.setName(name);
	// permissionEntity.setPath(path);
	// list.add(permissionEntity);
	// }
	// return list;
	// }
	// return null;
	//
	// }

	/**
	 * 获取【方法】注解中的内容
	 * 
	 * @param clazz
	 * @return 方法上的权限
	 * @throws Exception
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @auth nibili 2015年4月1日 下午11:16:36
	 */
	private List<UrlResourceEntity> getRequestMappingMethodAnotionValue(Class<?> clazz) throws Exception {
		Method[] methods = clazz.getMethods();
		String classAnotionPath = "";
		if (clazz.isAnnotationPresent(RequestMapping.class) == true) {
			RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
			classAnotionPath = requestMapping.value()[0];
		}
		// 权限集合
		List<UrlResourceEntity> list = Lists.newArrayList();
		UrlResourceEntity permissionEntity;
		// 方法路径
		for (Method method : methods) {
			// 权限名称，即注释名称
			String name = "";
			if (method.isAnnotationPresent(UrlReserveAnotion.class) == true) {
				UrlReserveAnotion permissionAnotion = method.getAnnotation(UrlReserveAnotion.class);
				name = permissionAnotion.value();
			}
			// 权限路径
			if (method.isAnnotationPresent(RequestMapping.class) == true) {
				RequestMapping classAnnotation = method.getAnnotation(RequestMapping.class);
				String[] paths = classAnnotation.value();
				for (String path : paths) {
					permissionEntity = new UrlResourceEntity();
					permissionEntity.setName(name);
					permissionEntity.setPath(classAnotionPath + path);
					list.add(permissionEntity);
				}
			}
		}
		return list;

	}

	/**
	 * 从包package中获取所有的Class
	 * 
	 * @param pack
	 * @return
	 */
	private List<Class<?>> getClasses(String packageName) {

		// 第一个class类的集合
		List<Class<?>> classes = new ArrayList<Class<?>>();
		// 是否循环迭代
		boolean recursive = true;
		// 获取包的名字 并进行替换
		String packageDirName = packageName.replace('.', '/');
		// 定义一个枚举的集合 并进行循环来处理这个目录下的things
		Enumeration<URL> dirs;
		try {
			dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
			// 循环迭代下去
			while (dirs.hasMoreElements()) {
				// 获取下一个元素
				URL url = dirs.nextElement();
				// 得到协议的名称
				String protocol = url.getProtocol();
				// 如果是以文件的形式保存在服务器上
				if ("file".equals(protocol)) {
					// 获取包的物理路径
					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
					// 以文件的方式扫描整个包下的文件 并添加到集合中
					findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
				} else if ("jar".equals(protocol)) {
					// 如果是jar包文件
					// 定义一个JarFile
					JarFile jar;
					try {
						// 获取jar
						jar = ((JarURLConnection) url.openConnection()).getJarFile();
						// 从此jar包 得到一个枚举类
						Enumeration<JarEntry> entries = jar.entries();
						// 同样的进行循环迭代
						while (entries.hasMoreElements()) {
							// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
							JarEntry entry = entries.nextElement();
							String name = entry.getName();
							// 如果是以/开头的
							if (name.charAt(0) == '/') {
								// 获取后面的字符串
								name = name.substring(1);
							}
							// 如果前半部分和定义的包名相同
							if (name.startsWith(packageDirName)) {
								int idx = name.lastIndexOf('/');
								// 如果以"/"结尾 是一个包
								if (idx != -1) {
									// 获取包名 把"/"替换成"."
									packageName = name.substring(0, idx).replace('/', '.');
								}
								// 如果可以迭代下去 并且是一个包
								if ((idx != -1) || recursive) {
									// 如果是一个.class文件 而且不是目录
									if (name.endsWith(".class") && !entry.isDirectory()) {
										// 去掉后面的".class" 获取真正的类名
										String className = name.substring(packageName.length() + 1, name.length() - 6);
										try {
											// 添加到classes
											classes.add(Class.forName(packageName + '.' + className));
										} catch (ClassNotFoundException e) {
											e.printStackTrace();
										}
									}
								}
							}
						}
					} catch (IOException e) {
						logger.error("", e);
					}
				}
			}
		} catch (IOException e) {
			logger.error("", e);
		}
		return classes;
	}

	/**
	 * 以文件的形式来获取包下的所有Class
	 * 
	 * @param packageName
	 * @param packagePath
	 * @param recursive
	 * @param classes
	 */
	private void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List<Class<?>> classes) {
		// 获取此包的目录 建立一个File
		File dir = new File(packagePath);
		// 如果不存在或者 也不是目录就直接返回
		if (!dir.exists() || !dir.isDirectory()) {
			return;
		}
		// 如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = dir.listFiles(new FileFilter() {

			// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
			public boolean accept(File file) {
				return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
			}
		});
		// 循环所有文件
		for (File file : dirfiles) {
			// 如果是目录 则继续扫描
			if (file.isDirectory()) {
				findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
			} else {
				// 如果是java类文件 去掉后面的.class 只留下类名
				String className = file.getName().substring(0, file.getName().length() - 6);
				try {
					// 添加到集合中去
					classes.add(Class.forName(packageName + '.' + className));
				} catch (ClassNotFoundException e) {
					logger.error("", e);
				}
			}
		}
	}

}
